
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <assert.h>

#include <linux/videodev.h>
#include <asm/types.h>


#include "capture.h"
#include "global.h"


struct camera* camera_alloc(char* device_name, int cam_num)
{
	struct camera* cam;

	cam = (struct camera*) malloc (sizeof(struct camera));
	if (!cam) {
		printf("Failed to camera alloc\n");
		return NULL;
	}
	memset(cam, 0, sizeof(struct camera));

	cam->is_update = 0;
	cam->cam_num = cam_num;
	cam->dev = -1;

	strcpy(cam->device_name, device_name);

	cam->rgb_data = (unsigned char*)malloc(sizeof(unsigned char)*width*height*3);
	if (!cam->rgb_data) {
		printf("Failed to camera rgb_data alloc\n");
		return NULL;
	}
	cam->pic_data = (unsigned char*)malloc(sizeof(unsigned char)*width*height*3/2);
	if (!cam->pic_data) {
		printf("Failed to camera pic_data alloc\n");
		return NULL;
	}

	if ((cam->dev = open(cam->device_name, O_RDWR | O_NOCTTY)) < 0) {
		fprintf(stderr, "video device could not be opened\n");
		return NULL;
	}

	return cam;
}

int dump_video_picture(struct camera* cam)
{
	int return_value;

	return_value = ioctl(cam->dev, VIDIOCGPICT, &(cam->pic));
	if (return_value < 0) { 
		fprintf(stderr,"Failed to get the image properties\n");
		return -1;
	}

	printf("Image properties are as follows:\n");
	printf("brightness = %hx", cam->pic.brightness);
	printf(", hue = %hx", cam->pic.hue);
	printf(", color = %hx", cam->pic.colour);
	printf(", contrast = %hx", cam->pic.contrast);
	printf(", whiteness = %hx", cam->pic.whiteness);
	printf(", depth = %hx", cam->pic.depth);
	printf(", palette = %hx\n", cam->pic.palette);

	return 1;
}
void dump_video_channel(struct camera* cam)
{

}
void dump_video_window(struct camera* cam)
{

}

int set_video_channel(struct camera* cam, int channel)
{
	int return_value;

	cam->chan.channel = channel;
	cam->chan.flags = 0;
	cam->chan.type = VIDEO_TYPE_CAMERA;
	cam->chan.norm = VIDEO_MODE_NTSC; 

	return_value = ioctl(cam->dev, VIDIOCSCHAN, &(cam->chan));

	if (return_value < 0) {
		fprintf(stderr, "ioctl on VIDIOCSCHAN failed\n");
		return -1;
	}

	return 1;
}
int set_video_window(struct camera* cam)
{
	int return_value;

	cam->win.width = width;
	cam->win.height = height;
	cam->win.x = cam->win.y = cam->win.chromakey = cam->win.flags = 0;

	return_value = ioctl(cam->dev, VIDIOCSWIN, &(cam->win));

	if (return_value < 0) {
		fprintf(stderr,"ioctl on VIDIOCSWIN failed\n");
		return -1;
	}

	return 1;
}

void set_video_picture(struct camera* cam, int depth, int palette)
{
	int return_value;

	cam->pic.brightness = 0xb800;
	cam->pic.contrast = 0xb800;
	cam->pic.depth = depth; 
	cam->pic.palette = palette; 

	return_value = ioctl(cam->dev, VIDIOCSPICT, &(cam->pic));
	if (return_value < 0) {
		fprintf(stderr,"Failed to set the image properties\n");
		return;
	}
}

int capture_init(struct camera* cam)
{
	int return_value, i;

	return_value = set_video_channel(cam, 0);
	if (return_value == -1) 
		return -1;

#if 0
	return_value = set_video_window(cam);
	if (return_value == -1) 
		return -1;

	dump_video_picture(cam);

	set_video_picture(cam, 8, VIDEO_PALETTE_YUV420P);
#endif
	return_value = ioctl(cam->dev, VIDIOCGMBUF, &(cam->m_buf));
	if (return_value < 0) {
		fprintf(stderr, "Failed to get information of capture memory space.\n");
		return -1;
	}

	cam->map = (char*) mmap(0, cam->m_buf.size, PROT_READ | PROT_WRITE, MAP_SHARED, cam->dev, 0);
	if(cam->map == MAP_FAILED) {
		fprintf(stderr,"Failed to get pointer to memory mapped area.\n");
		return -1;
	}
	cam->v_map.frame = 0;
	cam->v_map.height = height;
	cam->v_map.width = width;
	cam->v_map.format = VIDEO_PALETTE_YUV420P;
	for (i = 0; i < cam->m_buf.frames; i++) {
		cam->v_map.frame = i;
		return_value = ioctl(cam->dev, VIDIOCMCAPTURE, &(cam->v_map));
		if(return_value < 0) {
			fprintf(stderr, "Failed on VIDIOCMCAPTURE ioctl.\n");
			return -1;
		}
	}

	cam->fno = 0;
	for (i = 0; i < cam->m_buf.frames; i++) {
		return_value = capture(cam);
		if (return_value == -1)
			return -1;  
	}

	return 1;

}  	

int capture(struct camera* cam) 
{
	unsigned char *frame;

	cam->v_map.frame = cam->fno;
	if (ioctl(cam->dev, VIDIOCSYNC, &(cam->v_map.frame)) == -1) {
		fprintf(stderr, "(%d)Error : ioctl VIDIOCSYN\n", cam->dev);
		return -1;
	}

	frame = cam->map + cam->m_buf.offsets[cam->fno];
	cam->v_map.frame = cam->fno;

	if (ioctl(cam->dev, VIDIOCMCAPTURE, &(cam->v_map)) == -1) {
		fprintf(stderr, "Error : ioctl VIDIOCMCAPTURE\n");
		return -1;
	}

	cam->fno++;
	if (cam->fno >= cam->m_buf.frames)
		cam->fno = 0;

	cam->pic_data = frame;

	return 1;
}
void capture_close(struct camera* cam)
{
	if (cam) {
		if (cam->map)
			munmap(cam->map, cam->m_buf.size);
		if (cam->rgb_data)
			free(cam->rgb_data);
		if (cam->pic_data)
			free(cam->pic_data);
		if (cam->dev != -1)
			close(cam->dev);
		free(cam);
	}
}

